package com.hzw.saas.service.old.storage.service;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.old.storage.ICatalogService;
import com.hzw.saas.api.old.storage.IResourceFileService;
import com.hzw.saas.api.old.storage.bo.ResourceFileBO;
import com.hzw.saas.api.old.storage.bo.ResourceFileConflictBO;
import com.hzw.saas.api.old.storage.constants.StorageConstants;
import com.hzw.saas.api.old.storage.enums.ConflictModeEnum;
import com.hzw.saas.api.old.storage.enums.FileIntegrityEnum;
import com.hzw.saas.api.old.storage.enums.ResourceStatusEnum;
import com.hzw.saas.api.old.storage.exception.FileConflictException;
import com.hzw.saas.api.rss.ILibraryService;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.IdWorker;
import com.hzw.saas.service.old.hub.pojo.FileStorage;
import com.hzw.saas.service.old.hub.service.IFileStorageService;
import com.hzw.saas.service.old.storage.event.DeleteResourceFileEvent;
import com.hzw.saas.service.old.storage.mapper.OldResourceFileMapper;
import com.hzw.saas.service.old.storage.mapper.OldResourceFolderMapper;
import com.hzw.saas.service.old.storage.model.ResourceFile;
import com.hzw.saas.service.old.storage.model.ResourceFolder;
import com.hzw.saas.service.old.storage.util.StorageUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.json.JSONUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import lombok.RequiredArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.impl.DefaultMapperFactory;


/**
 * <p>
 * 资源表 服务实现类
 * </p>
 *
 * @author zzl
 * @since 2020-12-24
 */
@Service
@RequiredArgsConstructor
@DependsOn("springContextUtils")
public class OldResourceFileServiceImpl extends ServiceImpl<OldResourceFileMapper, ResourceFile> implements IResourceFileService, IService<ResourceFile> {

    @Resource
    private IResourceFileService self;

    @Autowired(required = false)
    @Qualifier("libraryService")
    private ILibraryService libraryService;

    private final ApplicationEventPublisher applicationEventPublisher;
    private final IdWorker idWorker;
    private final MapperFacade mapperFacade = new DefaultMapperFactory.Builder().mapNulls(false).build().getMapperFacade();
    private final OldResourceFolderMapper folderMapper;
    private final ICatalogService catalogService;
    private final IFileStorageService fileStorageService;
    private final IUserAccountService userAccountService;

    @Override
    public ResourceFileBO getFileInfo(String fileId, boolean throwNotFound) {
        if (StringUtils.isBlank(fileId)) return null;
        ResourceFile resourceFile = this.getById(fileId);
        AssertUtil.assertThrow(String.format("文件(%s)不存在", fileId), throwNotFound && resourceFile == null);
        ResourceFileBO resourceFileBO = mapperFacade.map(resourceFile, ResourceFileBO.class);
        if (resourceFileBO == null)
            return null;
        UserAccountBO creator = userAccountService.getAccount(resourceFileBO.getCreatorId());
        resourceFileBO.setCreatorName(creator == null ? "" : creator.getUserName());
        if (Objects.equals(resourceFileBO.getCreatorId(), resourceFileBO.getOwnerId())) {
            resourceFileBO.setOwnerName(resourceFileBO.getCreatorName());
        } else {
            UserAccountBO owner = userAccountService.getAccount(resourceFileBO.getOwnerId());
            resourceFileBO.setOwnerName(owner == null ? "" : owner.getUserName());
        }
        return resourceFileBO;
    }

    @Override
    public ResourceFileBO getFileInfo(String fileId, Integer status, Integer integrity, boolean throwNotFound) {
        ResourceFileBO fileBO = self.getFileInfo(fileId, throwNotFound);
        if (fileBO == null)
            return null;
        if (status != null && ObjectUtil.notEqual(fileBO.getStatus(), status)) {
            AssertUtil.assertThrow(String.format("文件(%s)状态异常：%s",
                fileId, ResourceStatusEnum.text(fileBO.getStatus())), throwNotFound);
            return null;
        }
        if (integrity != null && ObjectUtil.notEqual(fileBO.getIntegrity(), integrity)) {
            AssertUtil.assertThrow(String.format("文件(%s)完整性异常：%s",
                fileId, FileIntegrityEnum.text(fileBO.getIntegrity())), throwNotFound);
            return null;
        }
        return fileBO;
    }

    @Override
    public ResourceFileBO getFileInfoByNameInRoot(String ownerId, String catalog, String name, boolean throwNotFound) {
        String parentId = StorageConstants.ROOT_FOLDER_ID;
        return this.getNormalFileByName(ownerId, catalog, parentId, name, throwNotFound);
    }

    @Override
    public ResourceFileBO getFileInfoByName(String parentId, String name, boolean throwNotFound) {
        StorageUtil.isRootFolder(parentId, true);
        return this.getNormalFileByName(null, null, parentId, name, throwNotFound);
    }

    @Override
    public int totalCount(String ownerId, String catalog, Integer status, Integer integrity) {
        ResourceStatusEnum subInstance = ResourceStatusEnum.subInstance(status, false);
        Integer subStatus = subInstance == null ? null : subInstance.code();
        Integer count = this.lambdaQuery()
            .eq(ResourceFile::getOwnerId, ownerId)
            .eq(ResourceFile::getCatalog, catalog)
            .and(status != null, i -> i.eq(ResourceFile::getStatus, status)
                .or().eq(ResourceFile::getStatus, subStatus))
            .eq(integrity != null, ResourceFile::getIntegrity, integrity).count();
        return count;
    }

    @Override
    public List<ResourceFileBO> listFileInfo(List<String> fileIds, Integer status, Integer integrity, boolean throwNotFound) {
        if (CollectionUtil.isEmpty(fileIds))
            return new ArrayList<>();
        fileIds.removeIf(StringUtils::isBlank);
        List<ResourceFileBO> fileInfoList = new ArrayList<>();
        fileIds.forEach(id -> {
            ResourceFileBO fileInfo = this.getFileInfo(id, status, integrity, throwNotFound);
            if (Objects.nonNull(fileInfo)) {
                fileInfoList.add(fileInfo);
            }
        });
        return fileInfoList;
    }

    @Override
    public List<ResourceFileBO> listFileInfo(String ownerId, String catalog, Integer status, Integer integrity) {
        ResourceStatusEnum subInstance = ResourceStatusEnum.subInstance(status, false);
        Integer subStatus = subInstance == null ? null : subInstance.code();
        List<ResourceFile> list = this.lambdaQuery()
            .eq(ResourceFile::getOwnerId, ownerId)
            .eq(ResourceFile::getCatalog, catalog)
            .and(status != null, i -> i.eq(ResourceFile::getStatus, status)
                .or().eq(ResourceFile::getStatus, subStatus))
            .eq(integrity != null, ResourceFile::getIntegrity, integrity)
            .orderByDesc(ResourceFile::getUpdateDate)
            .list();
        return mapperFacade.mapAsList(list, ResourceFileBO.class);
    }

    @Override
    public List<ResourceFileBO> listFileInfoInRoot(String ownerId, String catalog, Integer status, Integer integrity) {
        String folderId = StorageConstants.ROOT_FOLDER_ID;
        return this.listFiles(ownerId, catalog, folderId, status, integrity);
    }

    @Override
    public List<ResourceFileBO> listFileInfoInFolder(String folderId, Integer status, Integer integrity) {
        StorageUtil.isRootFolder(folderId, true);
        return this.listFiles(null, null, folderId, status, integrity);
    }

    @Override
    public long totalSize(String ownerId, String catalog, Integer status, Integer integrity) {
        Wrapper<ResourceFile> queryWrapper = Wrappers.<ResourceFile>query().select("ifnull(sum(length), 0) as total")
            .lambda().eq(ResourceFile::getOwnerId, ownerId)
            .eq(ResourceFile::getCatalog, catalog)
            .eq(status != null, ResourceFile::getStatus, status)
            .eq(integrity != null, ResourceFile::getIntegrity, integrity);
        Map<String, Object> map = this.getMap(queryWrapper);
        Object total = map.get("total");
        return NumberUtil.parseLong(String.valueOf(total));
    }

    @Override
    public long countFilesSize(List<String> fileIds) {
        long totalSize = 0L;
        if (CollectionUtil.isEmpty(fileIds))
            return totalSize;
        for (String fileId : fileIds) {
            ResourceFileBO fileInfo = this.getFileInfo(fileId, ResourceStatusEnum.NORMAL.code(), FileIntegrityEnum.NORMAL.code(), false);
            if (Objects.isNull(fileInfo))
                continue;
            totalSize += Objects.isNull(fileInfo.getLength()) ? 0 : fileInfo.getLength();
        }
        return totalSize;
    }

    @Override
    public boolean checkAuthOwn(String ownerId, String fileId) {
        Integer count = this.lambdaQuery().eq(ResourceFile::getFileId, fileId).eq(ResourceFile::getOwnerId, ownerId).count();
        return ObjectUtil.compare(count, 0) > 0;
    }

    /**
     * 文件信息保存需进行以下校验
     * 1. 必要参数：文件名，所在目录，所有者
     * 2. 目录存在，且状态正常
     * 3. 同目录下无正常状态的同名文件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // @CachePut(cacheNames = FILE_CACHE_NAME, key = "#fileInfo.fileId")
    public ResourceFileBO saveFileInfo(ResourceFileBO fileInfo, ConflictModeEnum conflictHandleEnum) {
        this.checkSaveParam(fileInfo);
        ResourceFileBO fileExist = this.verifyFileName(fileInfo);
        if (fileExist != null) {
            switch (conflictHandleEnum) {
                case THROW_EXISTS:
                    throw FileConflictException.build("存在同名文件", new ResourceFileConflictBO(fileInfo, fileExist));
                case RETURN_EXISTS:
                    log.debug("待创建文件已存在，返回该文件：" + JSONUtil.toJsonStr(fileExist));
                    return fileExist;
                case COVER_EXISTS:
                    self.updateFilesStatus(fileExist.getFileId(), ResourceStatusEnum.DELETED_COVER.code(), false);
                    break;
                case MERGE_EXISTS:
                case RENAME_ORIGINAL:
                    String rename = this.getNextCopyName(fileExist.getOwnerId(), fileExist.getCatalog(), fileExist.getFolderId(), fileExist.getName());
                    log.debug("待创建文件已存在，重命名文件：" + rename);
                    fileInfo.setName(rename);
                    break;
                case ALLOW_EXISTS:
                    break;
            }
        }
        this.save(mapperFacade.map(fileInfo, ResourceFile.class));
        return fileInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResourceFileBO saveFile(ResourceFileBO fileBO, InputStream inputStream, ConflictModeEnum conflictHandleEnum) {
        ResourceFileBO resourceFileBO = this.saveFileInfo(fileBO, conflictHandleEnum);
        String supPath = this.catalogService.getFileParentPath(fileBO.getCatalog());
        FileStorage fileStorage = new FileStorage();
        fileStorage.setFileName(resourceFileBO.getResourceKey());
        fileStorage.setSuperPath(supPath);
        fileStorage.setLength(fileBO.getLength());
        fileStorage.setDigest(fileBO.getDigest());
        fileStorage.setDigestType(DigestAlgorithm.MD5.getValue());
        fileStorageService.saveFile(fileStorage, inputStream, com.hzw.saas.common.util.enums.ConflictModeEnum.THROW_ERR);
        return fileBO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResourceFileBO saveFile(ResourceFileBO fileBO, MultipartFile file, ConflictModeEnum conflictHandleEnum) {
        try {
            InputStream inputStream = file.getInputStream();
            return this.saveFile(fileBO, inputStream, conflictHandleEnum);
        } catch (IOException e) {
            throw DefaultSaasException.build("保存文件失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyFilesInfo(List<String> fileIds, String targetFolderId, ConflictModeEnum conflictHandleEnum) {
        String userId = SecurityUtils.getUser().getUserId();
        // 校验
        if (CollUtil.isEmpty(fileIds))
            return;
        // 去重
        fileIds = new ArrayList<>(new HashSet<>(fileIds));
        for (String fileId : fileIds) {
            // 目标目录与当前目录一样，无变动
            ResourceFileBO originalFile = self.getFileInfo(fileId, ResourceStatusEnum.NORMAL.code(), FileIntegrityEnum.NORMAL.code(), true);
            // 复制文件信息
            originalFile.setFileId(null);
            originalFile.setFolderId(targetFolderId);
            originalFile.setOwnerId(userId);
            originalFile.setCreatorId(userId);
            self.saveFileInfo(originalFile, conflictHandleEnum);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void moveFilesInfo(List<String> fileIds, String targetFolderId, ConflictModeEnum conflictHandleEnum) {
        // 校验
        if (CollUtil.isEmpty(fileIds))
            return;
        // 去重
        fileIds = new ArrayList<>(new HashSet<>(fileIds));
        for (String fileId : fileIds) {
            // 目标目录与当前目录一样，无变动
            ResourceFileBO originalFile = self.getFileInfo(fileId, ResourceStatusEnum.NORMAL.code(), FileIntegrityEnum.NORMAL.code(), true);
            if (Objects.equals(originalFile.getFolderId(), targetFolderId))
                continue;
            ResourceFileBO fileInfo = new ResourceFileBO()
                .setFileId(fileId)
                .setFolderId(targetFolderId);
            self.updateFileInfo(fileInfo, conflictHandleEnum, true);
        }
    }

    /**
     * 文件更新参数校验
     * 1. id不为空
     * 2. 不可为空字段防止更新为空
     * 3. 防止更新“删除”状态
     * 4. 若更新目录，则验证目录存在且状态正常
     * 5. 若更新文件名，则验证同目录下无正常状态的同名文件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // @CachePut(cacheNames = FILE_CACHE_NAME, key = "#fileInfo.fileId")
    public void updateFileInfo(ResourceFileBO fileInfo, ConflictModeEnum conflictHandleEnum, boolean throwDeleted) {
        this.checkUpdateParam(fileInfo, throwDeleted);
        ResourceFileBO fileExist = this.verifyFileName(fileInfo);
        if (fileExist != null) {
            switch (conflictHandleEnum) {
                case THROW_EXISTS:
                    throw FileConflictException.build("存在同名文件", new ResourceFileConflictBO(fileInfo, fileExist));
                case RETURN_EXISTS:
                    // 移动文件时，保留目标文件，删除原文件
                    self.updateFilesStatus(fileInfo.getFileId(), ResourceStatusEnum.DELETED_COVER.code(), false);
                    return;
                case COVER_EXISTS:
                    // 移动文件时，删除目标文件
                    self.updateFilesStatus(fileExist.getFileId(), ResourceStatusEnum.DELETED_COVER.code(), false);
                    break;
                case MERGE_EXISTS:
                case RENAME_ORIGINAL:
                    String rename = this.getNextCopyName(fileExist.getOwnerId(), fileExist.getCatalog(), fileExist.getFolderId(), fileExist.getName());
                    fileInfo.setName(rename);
                    break;
            }
        }
        ResourceFile fileDO = mapperFacade.map(fileInfo, ResourceFile.class);
        this.updateById(fileDO);
    }

    @Override
    // @CachePut(cacheNames = FILE_CACHE_NAME, key = "#fileId", unless = "#result == null")
    public void updateFileIntegrity(String fileId, Integer integrity) {
        if (StringUtils.isBlank(fileId)) return;
        FileIntegrityEnum.instance(integrity, true);
        // 检查当前完整性状态是否与待更新状态相同
        ResourceFileBO file = this.getFileInfo(fileId, true);
        if (Objects.equals(file.getIntegrity(), integrity))
            return;
        this.lambdaUpdate().set(ResourceFile::getIntegrity, integrity)
            .set(ResourceFile::getUpdateDate, new Date())
            .eq(ResourceFile::getFileId, fileId).update();
    }

    @Override
    // @CachePut(cacheNames = FILE_CACHE_NAME, key = "#fileId", unless = "#result == null")
    public void updateFilesStatus(String fileId, Integer status, boolean throwDeleted) {
        if (StringUtils.isBlank(fileId)) return;
        ResourceStatusEnum.instance(status, true);
        ResourceFileBO fileExist = this.getFileInfo(fileId, true);
        // 状态无变化，取消更新
        if (Objects.equals(fileExist.getStatus(), status))
            return;
        // 防止更新已被删除的目录状态
        if (ResourceStatusEnum.isDeleted(fileExist.getStatus()) && !ResourceStatusEnum.isDeleted(status)) {
            AssertUtil.assertThrow("不可操作已删除目录", throwDeleted);
            return;
        }
        this.lambdaUpdate().set(ResourceFile::getStatus, status)
            .set(ResourceFile::getUpdateDate, new Date())
            .eq(ResourceFile::getFileId, fileId).update();
        // 资源删除通知
        if (ResourceStatusEnum.isArchived(status) || ResourceStatusEnum.isDeleted(status)) {
            applicationEventPublisher.publishEvent(new DeleteResourceFileEvent(fileExist.getCreatorId(), fileId));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFilesStatus(List<String> fileIdList, Integer status, boolean throwDeleted) {
        if (fileIdList == null) return;
        fileIdList.forEach(id -> self.updateFilesStatus(id, status, throwDeleted));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFilesStatusInRoot(String ownerId, String catalog, Integer status) {
        List<ResourceFileBO> fileBOList = self.listFileInfoInRoot(ownerId, catalog, null, null);
        fileBOList.forEach(fileBO -> self.updateFilesStatus(fileBO.getFileId(), status, false));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFilesStatusInFolder(String folderId, Integer status) {
        List<ResourceFileBO> fileBOList = self.listFileInfoInFolder(folderId, null, null);
        fileBOList.forEach(fileBO -> self.updateFilesStatus(fileBO.getFileId(), status, false));
    }

    @Override
    public void deleteFile(String fileId) {
        if (StringUtils.isBlank(fileId))
            return;
        this.removeById(fileId);
    }

    @Override
    public void uploadFile(String fileId) {
    }

    /**
     * 下载文件需校验
     * 1. 文件存在且状态正常
     * 2. 文件完整性正常
     */
    @Override
    public void downloadFile(String catalog, String fileId, OutputStream outputStream, long startBytes) {
        ResourceFileBO fileInfo = this.getFileInfo(fileId, ResourceStatusEnum.NORMAL.code(), null, true);
        // 文件完整性正常
        FileIntegrityEnum.check(FileIntegrityEnum.NORMAL, fileInfo.getIntegrity(), true, true);
        fileStorageService.downloadFile(outputStream, startBytes - 1, fileInfo.getResourceKey(), catalogService.getFilePath(catalog));
    }

    @Override
    public File getFile(String fileId) {
        ResourceFileBO fileInfo = this.getFileInfo(fileId, ResourceStatusEnum.NORMAL.code(), FileIntegrityEnum.NORMAL.code(), true);
        return fileStorageService.getFile(fileInfo.getResourceKey(), catalogService.getFileParentPath(fileInfo.getCatalog()));
    }

    /**
     * 校验父目录是否存在
     * 当目录存在时，设定与目录相同的存储桶和所有者
     *
     * @param fileInfo
     */
    private void verifyParentFolder(ResourceFileBO fileInfo) {
        if (StringUtils.isNotBlank(fileInfo.getFolderId()) && !StorageUtil.isRootFolder(fileInfo.getFolderId(), false)) {
            ResourceFolder parentFolder = folderMapper.selectById(fileInfo.getFolderId());
            AssertUtil.assertThrow("父目录不存在：" + fileInfo.getFolderId(), parentFolder == null);
            AssertUtil.assertThrow("父目录已被删除：" + fileInfo.getFolderId(), HttpStatus.GONE,
                ResourceStatusEnum.isArchived(parentFolder.getStatus()) || ResourceStatusEnum.isDeleted(parentFolder.getStatus()));
            fileInfo.setCatalog(parentFolder.getCatalog());
            fileInfo.setOwnerId(parentFolder.getOwnerId());
        }
    }

    /**
     * 校验同名约束
     *
     * @param fileInfo 文件信息
     * @return 存在的同名文件
     */
    private ResourceFileBO verifyFileName(ResourceFileBO fileInfo) {
        // 获取已存在的文件信息
        ResourceFileBO fileOriginal = self.getFileInfo(fileInfo.getFileId(), false);
        if (fileOriginal != null) {
            mapperFacade.map(fileInfo, fileOriginal);
        } else {
            fileOriginal = fileInfo;
        }
        // 忽略非NORMAL状态的资源
        if (!Objects.equals(fileOriginal.getStatus(), ResourceStatusEnum.NORMAL.code()))
            return null;
        // 校验同名文件约束
        ResourceFileBO fileExist = this.getNormalFileByName(
            fileOriginal.getOwnerId(), fileOriginal.getCatalog(), fileOriginal.getFolderId(), fileOriginal.getName(), false);
        boolean conflict = fileExist != null && !Objects.equals(fileExist.getFileId(), fileOriginal.getFileId());
        if (conflict) {
            return fileExist;
        }
        return null;
    }

    /**
     * 文件信息保存参数校验
     */
    private void checkSaveParam(ResourceFileBO fileInfo) {
        // 校验不可为空
        AssertUtil.assertThrow("所在目录ID不可为空", StringUtils.isBlank(fileInfo.getFolderId()));
        AssertUtil.assertThrow("文件名不可为空", StringUtils.isBlank(fileInfo.getName()));
        AssertUtil.assertThrow("catalog不可为空", StringUtils.isBlank(fileInfo.getCatalog()));
        // 校验目录存在性
        this.verifyParentFolder(fileInfo);
        // 校验不可为空
        AssertUtil.assertThrow("所有者不可为空", StringUtils.isBlank(fileInfo.getOwnerId()));
        // 设置默认值
        Date now = new Date();
        fileInfo.setFileId(String.valueOf(idWorker.nextId()));
        if (StrUtil.isBlank(fileInfo.getResourceKey())) {
            fileInfo.setResourceKey(fileInfo.getFileId());
        }
        fileInfo.setSuffix(FileUtil.getSuffix(fileInfo.getName()));
        if (Objects.isNull(fileInfo.getStatus()))
            fileInfo.setStatus(ResourceStatusEnum.NORMAL.code());
        if (Objects.isNull(fileInfo.getIntegrity()))
            fileInfo.setIntegrity(FileIntegrityEnum.NORMAL.code());
        fileInfo.setCreateDate(now);
        fileInfo.setUpdateDate(now);
        if (StringUtils.isBlank(fileInfo.getCreatorId()))
            fileInfo.setCreatorId(fileInfo.getOwnerId());
    }

    /**
     * 文件信息更新参数校验
     */
    private void checkUpdateParam(ResourceFileBO fileInfo, boolean throwDeleted) {
        // 校验ID
        AssertUtil.assertThrow("文件ID为空", StringUtils.isBlank(fileInfo.getFileId()));
        // 检查不可为空字段是否被更新为空
        if (StrUtil.isBlank(fileInfo.getName())) {
            fileInfo.setName(null);
        } else {
            fileInfo.setSuffix(FileUtil.getSuffix(fileInfo.getName()));
        }
        if (StrUtil.isEmpty(fileInfo.getDigest()))
            fileInfo.setDigest(null);
        if (fileInfo.getStatus() != null)
            ResourceStatusEnum.instance(fileInfo.getStatus(), true);
        if (fileInfo.getIntegrity() != null)
            FileIntegrityEnum.instance(fileInfo.getIntegrity(), true);
        if (StrUtil.isBlank(fileInfo.getCatalog()))
            fileInfo.setCatalog(null);
        if (StrUtil.isBlank(fileInfo.getCreatorId()))
            fileInfo.setCreatorId(null);
        if (StrUtil.isBlank(fileInfo.getOwnerId()))
            fileInfo.setOwnerId(null);
        if (StrUtil.isEmpty(fileInfo.getChunkInfo()))
            fileInfo.setChunkInfo(null);
        // 设置更新时间
        fileInfo.setUpdateDate(new Date());

        ResourceFileBO fileExist = self.getFileInfo(fileInfo.getFileId(), true);
        // 防止更新已被删除的文件状态
        if (ResourceStatusEnum.isDeleted(fileExist.getStatus())) {
            AssertUtil.assertThrow("不可操作已删除文件", throwDeleted);
            fileInfo.setStatus(null);
        }
        // 校验目录存在性
        if (StrUtil.isBlank(fileInfo.getFolderId()))
            fileInfo.setFolderId(fileExist.getFolderId());
        this.verifyParentFolder(fileInfo);
    }

    private ResourceFileBO getNormalFileByName(String ownerId, String catalog, String parentId, String fileName, boolean throwNotFound) {
        boolean isRoot = StorageUtil.isRootFolder(parentId, false);
        List<ResourceFile> fileInfoList = this.lambdaQuery()
            .eq(isRoot, ResourceFile::getOwnerId, ownerId)
            .eq(isRoot, ResourceFile::getCatalog, catalog)
            .eq(ResourceFile::getFolderId, parentId)
            .eq(ResourceFile::getName, fileName)
            .eq(ResourceFile::getIntegrity, FileIntegrityEnum.NORMAL.code())
            .eq(ResourceFile::getStatus, ResourceStatusEnum.NORMAL.code()).list();
        AssertUtil.assertThrow("文件不存在：" + fileName, throwNotFound && fileInfoList.size() == 0);
        if (fileInfoList.size() == 0)
            return null;
        return mapperFacade.map(fileInfoList.get(0), ResourceFileBO.class);
    }

    private List<ResourceFileBO> listFiles(String ownerId, String catalog, String folderId, Integer status, Integer integrity) {
        ResourceStatusEnum subInstance = ResourceStatusEnum.subInstance(status, false);
        Integer subStatus = subInstance == null ? null : subInstance.code();
        boolean isRoot = StorageUtil.isRootFolder(folderId, false);
        List<ResourceFile> fileDOList = this.lambdaQuery()
            .eq(isRoot, ResourceFile::getCreatorId, ownerId)
            .eq(isRoot, ResourceFile::getCatalog, catalog)
            .eq(ResourceFile::getFolderId, folderId)
            .eq(integrity != null, ResourceFile::getIntegrity, integrity)
            .and(status != null, i -> i.eq(ResourceFile::getStatus, status)
                .or().eq(ResourceFile::getStatus, subStatus))
            .orderByDesc(ResourceFile::getUpdateDate)
            .list();
        List<String> fileIds = fileDOList.stream().map(ResourceFile::getFileId).collect(Collectors.toList());
        if (Objects.nonNull(libraryService)) {
            List<String> libraryFileIds = libraryService.filterArticleFileIds(fileIds);
            fileDOList = fileDOList.stream().filter(file -> !libraryFileIds.contains(file.getFileId())).collect(Collectors.toList());
        }
        return mapperFacade.mapAsList(fileDOList, ResourceFileBO.class);
    }

    private String getNextCopyName(String ownerId, String catalog, String parentId, String fileName) {
        String suffix = FileUtil.getSuffix(fileName);
        suffix = StringUtils.isBlank(suffix) ? suffix : ("." + suffix);
        fileName = fileName.substring(0, fileName.lastIndexOf(suffix));
        String nameSuffixHead = fileName + StorageConstants.COPY_SUFFIX_HEAD;
        boolean isRoot = StorageUtil.isRootFolder(parentId, false);
        List<ResourceFile> fileInfoList = this.lambdaQuery()
            .eq(isRoot, ResourceFile::getOwnerId, ownerId)
            .eq(isRoot, ResourceFile::getCatalog, catalog)
            .eq(ResourceFile::getFolderId, parentId)
            .eq(ResourceFile::getStatus, ResourceStatusEnum.NORMAL.code())
            .likeRight(ResourceFile::getName, nameSuffixHead)
            .orderByDesc(ResourceFile::getName).list();
        for (ResourceFile fileInfo : fileInfoList) {
            String name = fileInfo.getName();
            String indexStr = name.substring(nameSuffixHead.length(), name.lastIndexOf(StorageConstants.COPY_SUFFIX_END));
            try {
                int index = Integer.parseInt(indexStr) + 1;
                return nameSuffixHead + index + StorageConstants.COPY_SUFFIX_END + suffix;
            } catch (NumberFormatException e) {
                continue;
            }
        }
        return nameSuffixHead + 1 + StorageConstants.COPY_SUFFIX_END + suffix;
    }

}
